import { RenderAdapterShape, RendererShape } from '@/common/render';
import { useDocumentStore, useRenderStore } from '@/common/store';
import { ModelTreeObjectType } from '@/common/types';
import { nmAppDebugLog, nmAppErrorLog } from '@/common/utils';
import {
  FeatureManager,
  getQueryParam,
  httpV3,
  QueryParamKeys,
} from '@amcax/base';
import { EntityManager, VisualData } from '@amcax/web-render';
import { IEntity } from '@amcax/web-render/src/entity';
import { throttle } from 'lodash';
import { ISocketEvent, ManagerId, SocketEventType } from 'nextcae-base';
import { basic } from '.';
import { filterRecurve } from './utils';

/**
 * 递归处理模型树对象的可见性状态
 * 根据对象的visibility属性和父节点可见性控制实体显示/隐藏
 * 对需要显示但尚未加载的模型，收集其ID以便后续请求后端获取
 *
 * @param mgr - 实体管理器，用于操作场景中的实体可见性
 * @param objects - 模型树对象数组，包含需要处理可见性的模型节点
 * @param requestObjects - 用于收集需要请求后端获取的模型ID的数组
 * @param focusHide - 可选参数，标记子节点是否应被强制隐藏
 *                       当祖先节点不可见时，此参数会传递给所有后代节点
 */
const recursiveVisibility = (
  mgr: EntityManager,
  objects: ModelTreeObjectType[],
  requestObjects: string[],
  focusHide?: boolean,
) => {
  objects.forEach((object) => {
    const visibility = object.visibility;
    // 如果当前对象需要显示且不受父节点隐藏影响
    if (visibility && !focusHide) {
      // 检查实体是否已存在
      if (mgr.hasEntity(object.dxid)) {
        // 实体存在，设置为可见
        mgr.setEntityVisible(true, object.dxid);
      } else {
        // 实体不存在，添加到请求列表
        requestObjects.push(object.dxid);
      }
    } else {
      // 当前对象需要隐藏或受父节点隐藏影响，设置为不可见
      mgr.setEntityVisible(false, object.dxid);
    }
    // 如果有子节点，递归处理子节点可见性
    if (object.children) {
      recursiveVisibility(
        mgr,
        object.children,
        requestObjects,
        // 子节点隐藏条件：当前隐藏条件或当前节点不可见
        focusHide || object.visibility === false,
      );
    }
  });
};
// 获取网格渲染数据回调函数
const getMeshData = async (ids?: string[]) => {
  const data = await basic.renderData(ids);
  return Array.isArray(data) ? data : [];
};
// 创建网格渲染适配器
const shapeAdapter = new RenderAdapterShape(getMeshData, new RendererShape());

/**
 * 添加或更新entity，并根据autofit是否调整视角
 * @param entities 需要添加或更新的entities
 */
const addOrUpdateEntities = async (entities: IEntity[], autofit: boolean) => {
  const amcaxRender = useRenderStore.getState().mainRenderer;
  const meshMgr = amcaxRender?.getEntityManager(ManagerId.Mesh);
  // 将工厂里获取的实体直接添加到EntityManager中即可进行渲染
  if (entities?.length > 0) {
    // 网格暂时没有做update，所以这里先删除，再添加
    meshMgr?.removeEntity(entities.map((e) => e.getDxid()));
    meshMgr?.addEntity(...entities);
    if (autofit) {
      // 可能会频繁调用，所以不执行动画，否则会出bug，导致后续的动画无效（比如：viewCube上的）
      await amcaxRender?.cameraManager.fitView(true, [0.4, -1, 0.6], false);
    } else {
      // 只调整视区大小使得模型不被裁剪，不改变相机视角
      await amcaxRender?.cameraManager.fitView(false, undefined, false);
    }
  }
};

/**
 * 通过ids请求后端来拉取渲染数据并渲染
 */
async function updateShape(ids: string[]) {
  const amcaxRender = useRenderStore.getState().mainRenderer;
  // 将渲染适配器传递给实体工厂，并获取entities
  const entityFactory = amcaxRender?.getEntityFactory();
  const entities = await entityFactory?.getEntities(shapeAdapter, ids);
  const autofit = useDocumentStore.getState().autoFitView;
  await addOrUpdateEntities(entities, autofit);
}

/**
 * 防抖截取并更新缩略图到平台
 */
const updateThumbnail = throttle(async () => {
  const collectionId = getQueryParam<string>(QueryParamKeys.COLLECTION_ID);
  const documentId = getQueryParam<string>(QueryParamKeys.DOCUMENT_ID);
  const version = getQueryParam<number>(QueryParamKeys.VERSION);
  // 根据 uuid 查找 id
  const { data } = await httpV3.post('/api/v3/documents/detailInfoByUuid', {
    collectionUuid: collectionId,
    documentUuid: documentId,
    version,
  });
  const amcaxRender = useRenderStore.getState().mainRenderer;
  const imageBase64 = amcaxRender?.getScreenshotImage();
  if (imageBase64) {
    const img = imageBase64.split('base64,')[1];
    await httpV3.post('/api/v3/cad/doc/thumbnail', {
      img,
      collectionId: data.collectionId,
      documentId: data.id,
    });
  }
}, 1000);

/**
 * 处理shapechangepending事件
 * @param data 事件包含的数据
 */
const handleChangePending = async (data: {
  added?: string[]; // 新增模型时，比如：导入/生成模型
  shapechanged?: string[]; // 模型发生改变时，比如：模型更新
  propertychanged?: string[]; // 属性发生改变时，比如：可见性改变
  visibilitychanged?: string[]; // 可见性发生改变时，比如：模型显示/隐藏
  removed?: string[]; // 删除对象
}) => {
  const amcaxRender = useRenderStore.getState().mainRenderer;
  const meshMgr = amcaxRender?.getEntityManager(ManagerId.Mesh);

  const oldModelTreeInfo = useDocumentStore.getState().modelTreeInfo;
  const hasVisibleModel =
    oldModelTreeInfo?.findIndex((info) => info.visibility) >= 0;

  // 全量更新模型树
  const modelTreeInfo = await basic.getObjects();
  useDocumentStore.getState().setModelTreeInfo(modelTreeInfo);

  const { added, shapechanged, propertychanged, visibilitychanged, removed } =
    data;

  if (propertychanged) {
    // 目前只要接受到 shapechangepending 事件，就会全量更新模型树，所以这里暂时没有做操作
  }

  if (added?.length > 0) {
    // 有需要添加的模型，但没有可见的模型，则自适应视图
    nmAppDebugLog('处理shapechangepending事件：添加模型', {
      added,
      hasVisibleModel,
    });
    useDocumentStore.getState().setAutoFitView(!hasVisibleModel);
    await updateShape(added);
  }
  if (shapechanged?.length > 0) {
    // 有模型发生改变，需要请求后端来更新模型
    await updateShape(shapechanged);
  }
  if (visibilitychanged) {
    // 递归查询模型树中属性改变的对象
    const changedObjects = filterRecurve(modelTreeInfo, 'children', (info) =>
      visibilitychanged.includes(info.dxid),
    );
    // 需要请求后端的对象
    const requestObjects: string[] = [];
    // 递归处理可见性变化
    recursiveVisibility(meshMgr, changedObjects, requestObjects);
    if (requestObjects.length > 0) {
      // 有需要显示的模型，但当前没有可见的模型，则自适应视图
      nmAppDebugLog('处理shapechangepending事件：显示模型', {
        requestObjects,
        hasVisibleModel,
      });
      useDocumentStore.getState().setAutoFitView(!hasVisibleModel);
      await updateShape(added);
    }
  }
  if (removed) {
    meshMgr.removeEntity(removed);
  }
  amcaxRender?.render();
  if (FeatureManager.isFeatureEnabled('web')) {
    await updateThumbnail();
  }
};

/**
 * 处理 geometryRender 模型渲染事件
 * @param data 推送过来的渲染数据
 */
export const handleGeometryRender = async (data: VisualData[]) => {
  const autofit = useDocumentStore.getState().autoFitView;

  const amcaxRender = useRenderStore.getState().mainRenderer;
  const entities = new RendererShape().render(shapeAdapter.convert(data));
  await addOrUpdateEntities(entities, autofit);
  amcaxRender.render();
};

/**
 * 处理Socket事件
 * @param event
 */
export async function handleSocketEvent(event: ISocketEvent) {
  try {
    switch (event.event) {
      case SocketEventType.shapechangepending:
        await handleChangePending(event.data);
        break;
      case SocketEventType.geometryRender:
        await handleGeometryRender(event.data);
        break;
    }
  } catch (error) {
    nmAppErrorLog('handleSocketEvent error', error);
  }
}
